package com.sinodata.bsm.cicp.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;

import org.apache.log4j.Logger;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

import com.sinodata.bsm.cicp.util.pool.SSH2ConnPool;
import com.sinodata.bsm.cicp.util.pool.SSHConnPool;
import com.sinodata.bsm.cicp.util.pool.TelnetConnPool;
import com.sinodata.bsm.cicp.util.telnet.EthzSSH;
import com.sinodata.bsm.cicp.util.telnet.JtaSSH;
import com.sinodata.bsm.cicp.util.telnet.JtaTelnet;
import com.sinodata.bsm.cicp.util.telnet.PromptFilter;
import com.sinodata.bsm.common.constants.ProtocolConstants;
import com.sinodata.bsm.common.utils.DateUtil;
import com.sinodata.bsm.common.vo.ProtocolParameter;

/**
 * 
 * <p>
 * Description: BSM系统远程采集
 * </p>
 *
 * @author liulibo
 * @version 1.0

 * <p>
 * History: 
 *
 * Date                     Author         Version     Description
 * ---------------------------------------------------------------------------------
 * 2012-5-11 PM 4:39:02     liulibo        1.0         To create
 * </p>
 *
 * @since 
 * @see
 */
public class CommandReader {

    private static Logger logger = Logger.getLogger("prop");
    private static Logger cmdLogger = Logger.getLogger("prop");
    private static String localIP = null;
    private static int defaultCmdPort = 9001;
    private final static int DEFAULT_TIMEOUT = 60 * 1000;
    private static final String DATE_FORMATE = "yyyy-MM-dd HH:mm:ss.S";

    public static BufferedReader getLocalReader(String cmd, int dataTimeout, int soTimeout) throws IOException {
        //        if (localIP == null) {
        //            try {
        //                localIP = InetAddress.getLocalHost().getHostAddress().toString();
        //            } catch (UnknownHostException e) {
        //                localIP = "localhost";
        //            }
        //        }
        //        return getReader(localIP, cmd, dataTimeout, soTimeout);
        return LocalReader.getLocalReader(cmd);
    }

    /**
     * 适用于连接测试，根据输入的用户名密码建立连接执行命令，不缓存数据
     * 
     * @param ip
     * @param cmd
     * @param dataTimeout
     * @param soTimeout
     * @param port
     * @param maxConnCount
     * @param connType
     * @param user
     * @param pwd
     * @param prompt
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(String ip, String cmd, int dataTimeout, int soTimeout, int port, int maxConnCount, String connType, String user, String pwd, String prompt) throws IOException {
        if (connType == null) {
            connType = "";
        }
        // 根据采集方式选择使用的采集类，无法取得采集方式时返回空
        if (ProtocolConstants.TELNET.equals(connType)) {
            return getReaderByTelnet(ip, port, maxConnCount, cmd, dataTimeout, user, pwd, prompt);
        }
        if (ProtocolConstants.SSH.equals(connType)) {
            return getReaderBySsh(ip, port, maxConnCount, cmd, dataTimeout, user, pwd, prompt);
        }
        if (ProtocolConstants.SSH2.equalsIgnoreCase(connType)) {
            return getReaderBySsh2(ip, port, maxConnCount, cmd, dataTimeout, user, pwd, prompt);
        }
        return null;
    }

    /**
     * 通过不同的采集方式执行命令，得到一个reader。
     * 
     * @param ip 主机的IP地址
     * @param cmd 执行的命令
     * @param dataTimeout 通信层缓存数据的超时时间
     * @param soTimeout socket超时时间
     * @return
     * @throws IOException
     */
    public static BufferedReader getReader(String ip, String cmd, int dataTimeout, int soTimeout, String... charactSet) throws IOException {
        int port = defaultCmdPort;
        int maxConnCount = 10;
        String connType = "";
        ProtocolParameter protocolParameter = ProtocolParameterUtil.getHostValue(ip);
        if (protocolParameter != null) {
            port = protocolParameter.getPort();
            maxConnCount = protocolParameter.getMaxConn();
            connType = protocolParameter.getProtocol();
        }
        Object cache = ValueBuffer.getInstance().get(ip + "&" + port + "&" + cmd, dataTimeout);
        if (cache != null) {
            return new BufferedReader(new StringReader((String) cache));
        }
        // 根据采集方式选择使用的采集类，无法取得采集方式时返回空
        if (ProtocolConstants.TELNET.equals(connType)) {
            return getReaderByTelnet(ip, port, maxConnCount, cmd, dataTimeout);
        }
        if (ProtocolConstants.SSH.equals(connType)) {
            return getReaderBySsh(ip, port, maxConnCount, cmd, dataTimeout);
        }
        if (ProtocolConstants.SSH2.equalsIgnoreCase(connType)) {
            return getReaderBySsh2(ip, port, maxConnCount, cmd, dataTimeout, charactSet);
        }
        return null;
    }

    /**
     * 通过Telnet协议采集数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @return
     * @throws IOException
     */
    private static BufferedReader getReaderByTelnet(String ip, int port, int maxConnCount, String cmd, int dataTimeout) throws IOException {
        // 准备主机用户名密码
        String user = "bsm";
        String pwd = "bsm";
        String prompt = "$";
        ProtocolParameter protocolParameter = ProtocolParameterUtil.getHostValue(ip);
        if (protocolParameter != null) {
            user = protocolParameter.getUserName();
            pwd = protocolParameter.getPwd();
            prompt = protocolParameter.getPrompt();
        }
        // 1.从Telnet连接池获取Telnet连接 2.执行命令 3.释放链接
        JtaTelnet telnet = null;

        try {
            telnet = TelnetConnPool.getInstance().getTelnet(ip, port, user, pwd, maxConnCount, prompt);
            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            String shellOut = telnet.runTask(cmd);
            shellOut = PromptFilter.trimCommandPrompt(shellOut, cmd, prompt);
            byte b0[] = { 0x00 };
            shellOut = shellOut.replaceAll(new String(b0), "");
            String endTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(shellOut);
            ValueBuffer.getInstance().put(ip + "&" + port + "&" + cmd, strBuff.toString());//缓存数据
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + endTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            telnet.setIsValid(false);
            throw new IOException(t.getMessage());
        } finally {
            try {
                if (telnet != null) {
                    TelnetConnPool.getInstance().free(ip, telnet);
                }
            } catch (Exception e) {
                logger.error("Failed when free a telnetConnection", e);
            }
        }
    }

    /**
     * 适用于连接测试，根据输入的用户名密码建立连接执行命令，不缓存数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @param user
     * @param pwd
     * @param prompt
     * @return
     * @throws IOException
     */
    public static BufferedReader getReaderByTelnet(String ip, int port, int maxConnCount, String cmd, int dataTimeout, String user, String pwd, String prompt) throws IOException {

        // 1.从Telnet连接池获取Telnet连接 2.执行命令 3.释放链接
        // 1.从Telnet连接池获取Telnet连接 2.执行命令 3.释放链接
        JtaTelnet telnet = null;
        try {
            telnet = new JtaTelnet();
            telnet.connect(ip, port);
            telnet.login(user, pwd, prompt);
            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            String shellOut = telnet.runTask(cmd);
            shellOut = PromptFilter.trimCommandPrompt(shellOut, cmd, prompt);
            byte b0[] = { 0x00 };
            shellOut = shellOut.replaceAll(new String(b0), "");
            String endTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(shellOut);
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + endTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            throw new IOException(t.getMessage());
        } finally {
            try {
                if (telnet != null) {
                    telnet.disconnect();
                }
            } catch (Exception e) {
                logger.error("Failed when free a telnetConnection", e);
            }
        }
    }

    /**
     * 通过SSH协议采集数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @return
     * @throws IOException
     */
    private static BufferedReader getReaderBySsh(String ip, int port, int maxConnCount, String cmd, int dataTimeout) throws IOException {
        // 准备主机用户名密码
        String user = "bsm";
        String pwd = "bsm";
        String prompt = "";
        ProtocolParameter protocolParameter = ProtocolParameterUtil.getHostValue(ip);
        if (protocolParameter != null) {
            user = protocolParameter.getUserName();
            pwd = protocolParameter.getPwd();
            prompt = protocolParameter.getPrompt();
        }

        // 1.从SSH连接池获取SSH连接 2.执行命令 3.释放链接
        JtaSSH ssh = null;
        try {
            //            ssh = SSHPool.getInstance().getTelnet(ip, port, user, pwd, maxConnCount, prompt);

            ssh = SSHConnPool.getInstance().getTelnet(ip, port, user, pwd, maxConnCount, prompt);

            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            String shellOut = ssh.runTask(cmd);
            String endTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(shellOut);
            ValueBuffer.getInstance().put(ip + "&" + port + "&" + cmd, strBuff.toString());//缓存数据
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + endTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            ssh.setIsValid(false);
            throw new IOException(t.getMessage());
        } finally {
            try {
                if (ssh != null) {
                    SSHConnPool.getInstance().free(ip, ssh);
                    //                    SSHPool.getInstance().freeTelnet(ip, ssh);
                }
            } catch (Exception e) {
                logger.error("Failed when free a sshConnection", e);
            }
        }
    }

    /**
     * 适用于连接测试，根据输入的用户名密码建立连接执行命令，不缓存数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @param user
     * @param pwd
     * @param prompt
     * @return
     * @throws IOException
     */
    private static BufferedReader getReaderBySsh(String ip, int port, int maxConnCount, String cmd, int dataTimeout, String user, String pwd, String prompt) throws IOException {

        // 1.从SSH连接池获取SSH连接 2.执行命令 3.释放链接
        JtaSSH ssh = null;
        try {
            ssh = new JtaSSH();
            ssh.connect(ip, port);
            ssh.login(user, pwd, prompt);
            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            String shellOut = ssh.runTask(cmd);
            String endTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(shellOut);
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + endTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            ssh.setIsValid(false);
            throw new IOException(t.getMessage());
        } finally {
            try {
                if (ssh != null) {
                    ssh.disconnect();
                }
            } catch (Exception e) {
                logger.error("Failed when free a sshConnection", e);
            }
        }
    }

    /**
     * 通过SSH2协议采集数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @return
     * @throws IOException
     */
    private static BufferedReader getReaderBySsh2(String ip, int port, int maxConnCount, String cmd, int dataTimeout, String... charactSet) throws IOException {
        // 准备主机用户名密码
        String user = "bsm";
        String pwd = "bsm";
        //        String prompt = "";
        ProtocolParameter protocolParameter = ProtocolParameterUtil.getHostValue(ip);

        if (protocolParameter != null) {
            user = protocolParameter.getUserName();
            pwd = protocolParameter.getPwd();
            //                    prompt = factory.getParameterValue(0, ip, "prompt");
        }

        EthzSSH ssh2 = null;
        Session session = null;
        Connection conn = null;
        try {
            ssh2 = SSH2ConnPool.getInstance().getConnection(ip, port, user, pwd, maxConnCount);
            conn = ssh2.getConn();
            session = conn.openSession();
            session.execCommand(cmd);
            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuilder sb = new StringBuilder();
            String tmpStr = "";
            InputStream stdout = new StreamGobbler(session.getStdout());
            //InputStream stdout = new StreamGobbler(session.getStderr());

            BufferedReader br = new BufferedReader(new InputStreamReader(stdout, charactSet != null && charactSet.length > 0 && charactSet[0] != null ? charactSet[0] : "GBK"));
            while ((tmpStr = br.readLine()) != null) {
                sb.append(tmpStr + "\n");
            }
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                }
                br = null;
            }
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(sb.toString());
            ValueBuffer.getInstance().put(ip + "&" + port + "&" + cmd, strBuff.toString());//缓存数据
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;// succeed
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            if (ssh2 != null) {
                ssh2.setValid(false);
            }
            throw new IOException(t.getMessage());
        } finally {
            try {
                //session在每次执行命令后都要关闭
                if (session != null) {
                    session.close();
                }
                if (ssh2 != null) {
                    SSH2ConnPool.getInstance().free(ip, ssh2);
                }
            } catch (Exception e) {
                logger.error("Failed when free a ssh2Connection", e);
            }
        }
    }

    /**
     * 适用于连接测试，根据输入的用户名密码建立连接执行命令，不缓存数据
     * 
     * @param ip
     * @param port
     * @param maxConnCount
     * @param cmd
     * @param dataTimeout
     * @param user
     * @param pwd
     * @param prompt
     * @return
     * @throws IOException
     */
    private static BufferedReader getReaderBySsh2(String ip, int port, int maxConnCount, String cmd, int dataTimeout, String user, String pwd, String prompt) throws IOException {
        EthzSSH ssh2 = null;
        Session session = null;
        Connection conn = null;
        try {
            ssh2 = new EthzSSH();
            ssh2.login(ip, port, user, pwd);
            conn = ssh2.getConn();
            session = conn.openSession();
            session.execCommand(cmd);
            String startTime = DateUtil.format(new Date(), DATE_FORMATE);
            StringBuilder sb = new StringBuilder();
            String tmpStr = "";
            InputStream stdout = new StreamGobbler(session.getStdout());
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            while ((tmpStr = br.readLine()) != null) {
                sb.append(tmpStr + "\n");
            }
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                }
                br = null;
            }
            StringBuffer strBuff = new StringBuffer();
            strBuff.append(sb.toString());
            StringReader strReader = new StringReader(strBuff.toString());
            BufferedReader buffReader = new BufferedReader(strReader);
            cmdLogger.info("[SUCCEED]\t" + startTime + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            return buffReader;// succeed
        } catch (Throwable t) {
            String time = DateUtil.format(new Date(), DATE_FORMATE);
            cmdLogger.info("[FAILED]\t" + time + "\t" + ip + "\t" + user + "\t" + port + "\t" + cmd);
            if (ssh2 != null) {
                ssh2.setValid(false);
            }
            throw new IOException(t.getMessage());
        } finally {
            try {
                //session在每次执行命令后都要关闭
                if (session != null) {
                    session.close();
                }
                if (ssh2 != null) {
                    ssh2.disconn();
                }
            } catch (Exception e) {
                logger.error("Failed when free a ssh2Connection", e);
            }
        }
    }

    /**
     * 获取本地的IP
     * 
     * @return
     */
    @SuppressWarnings("unused")
    private static String getLocalhostIp() {
        if (localIP == null) {
            try {
                localIP = InetAddress.getLocalHost().getHostAddress().toString();
            } catch (UnknownHostException e) {
                localIP = "localhost";
            }
        }
        return localIP;
    }

    public static String getHostType(String ip) {
        if (ip == null) {
            return null;
        }
        BufferedReader reader = null;
        try {
            reader = CommandReader.getReader(ip, "uname", 0, 0);
            if (reader != null) {
                return reader.readLine().trim();
            }
        } catch (IOException e) {
            logger.error("failed to execute getHostType", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("failed to close reader in the method of CommandReader.getHostType", e);
                }
            }
        }

        return null;
    }
}